package com.hzzc.sso.sdk;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.hzzc.BizException;
import com.hzzc.Constant;
import com.hzzc.EnumResultCode;
import com.hzzc.Result;
import com.hzzc.api.CheckBean;
import com.hzzc.api.LogoutBean;
import com.hzzc.sso.sdk.session.SessionManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;

import javax.servlet.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;

@Slf4j
@Order(1)
public class SsoWebFilter implements Filter {

    private static final AntPathMatcher antPathMatcher = new AntPathMatcher();

    private static final ObjectMapper objectMapper = new ObjectMapper();

    @Autowired
    private SessionManager sessionManager;

    @Autowired
    private SsoWebSdkProperties ssoWebSdkProperties;

    @Autowired
    private RemoteUserAuthorityService remoteUserAuthorityService;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("SSO登录过滤器初始化成功");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest tempHttpServletRequest = (HttpServletRequest) request;
        String authCode = getAuthCode(tempHttpServletRequest);
        HttpServletRequestSessionWrappr httpServletRequest = new HttpServletRequestSessionWrappr(tempHttpServletRequest, sessionManager, authCode);
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;

        String requestCode = httpServletRequest.getRequestedSessionId();
        sessionManager.setLocalCode(requestCode);
        addCodeToCookie(httpServletRequest, httpServletResponse);

        boolean isJson = isJson(httpServletRequest);

        if (isExcluded(httpServletRequest)) {
            chain.doFilter(httpServletRequest, response);
        } else if (isGotoLogout(httpServletRequest)) {
            logout(httpServletRequest, httpServletResponse);
        } else {
            Result<String> authorityCheckResult = checkAuthority(httpServletRequest);
            if (EnumResultCode.SUCCESS.getCode() == authorityCheckResult.getCode()) {
                //权限满足
                chain.doFilter(httpServletRequest, response);
            } else if (EnumResultCode.UNAUTH.getCode() == authorityCheckResult.getCode()) {
                if (isJson) {
                    httpServletResponse.setContentType("application/json;charset=utf-8");
                    httpServletResponse.getWriter().println(objectMapper.writeValueAsString(Result.auth("", "跳转sso服务器授权！")));
                } else {
                    //未登录，跳转到统一登录页面
                    String ssoServer = ssoWebSdkProperties.getSsoServer();
                    String ssoLoginPage = ssoWebSdkProperties.getSsoLoginPage();
                    String redirectPath = ssoServer + ssoLoginPage + "?to=" + createTargetPath(httpServletRequest);
                    httpServletResponse.sendRedirect(redirectPath);
                }
            } else {
                if (isJson) {
                    httpServletResponse.setContentType("application/json;charset=utf-8");
                    httpServletResponse.getWriter().println(objectMapper.writeValueAsString(authorityCheckResult));
                } else {
                    if (StringUtils.isEmpty(ssoWebSdkProperties.getErrorShowPath())) {
                        throw new BizException(authorityCheckResult.getMsg());
                    } else {
                        httpServletResponse.sendRedirect(ssoWebSdkProperties.getErrorShowPath() + "?msg=" + authorityCheckResult.getMsg());
                    }
                }
            }
        }
        sessionManager.removeLocalCode();
    }

    /**
     * 如果当前请求有code,但是cookie中无code值,保险起见将code加入cookie,防止code丢失
     *
     * @param httpServletRequest
     * @param httpServletResponse
     */
    private void addCodeToCookie(HttpServletRequestSessionWrappr httpServletRequest, HttpServletResponse httpServletResponse) {
        String code = getDataFromCookie("code", httpServletRequest);
        String requestedSessionId = httpServletRequest.getRequestedSessionId();
        if (StringUtils.isEmpty(code) && !StringUtils.isEmpty(requestedSessionId)) {
            Cookie cookie = new Cookie("code", requestedSessionId);
            cookie.setMaxAge((int) (ssoWebSdkProperties.getSessionDuration() / 1000));
            cookie.setPath("/");
            httpServletResponse.addCookie(cookie);
        }
    }

    public String getAuthCode(HttpServletRequest httpServletRequest) {
        String sessionId = StringUtils.isEmpty(httpServletRequest.getParameter(Constant.code))
                ? (StringUtils.isEmpty(httpServletRequest.getHeader(Constant.code)) ? getDataFromCookie(Constant.code, httpServletRequest) : httpServletRequest.getHeader(Constant.code))
                : httpServletRequest.getParameter(Constant.code);
        return sessionId;
    }


    private String getDataFromCookie(String name, HttpServletRequest httpServletRequest) {
        Cookie[] cookies = httpServletRequest.getCookies();
        if (cookies == null || cookies.length == 0) {
            return null;
        }
        for (Cookie cookie : cookies) {
            if (cookie.getName().toLowerCase().equals(name)) {
                return cookie.getValue();
            }
        }
        return null;
    }

    private boolean isGotoLogout(HttpServletRequest httpServletRequest) {
        String resource = httpServletRequest.getRequestURI();
        return resource.equals(ssoWebSdkProperties.getLogoutPath());
    }


    /**
     * 检测权限
     *
     * @param httpServletRequest
     * @return
     */
    private Result<String> checkAuthority(HttpServletRequest httpServletRequest) {
        String requestCode = httpServletRequest.getRequestedSessionId();
        String requestURI = httpServletRequest.getRequestURI();
        return remoteUserAuthorityService.checkAuthority(new CheckBean().setApp(ssoWebSdkProperties.getApp()).setResource(requestURI).setCode(requestCode));
    }


    private String createTargetPath(HttpServletRequest request) {
        try {
            return URLEncoder.encode(request.getRequestURL().toString() + (StringUtils.isEmpty(request.getQueryString()) ? "" : "?" + request.getQueryString()), "utf-8");
        } catch (Exception e) {
            throw new BizException("编码URL异常！", e);
        }
    }

    /**
     * 判断当前地址是否需要忽略
     *
     * @param httpServletRequest
     * @return
     */
    private boolean isExcluded(HttpServletRequest httpServletRequest) {
        String requestPath = httpServletRequest.getRequestURI();
        if (ssoWebSdkProperties.getExcludeds() != null && ssoWebSdkProperties.getExcludeds().size() > 0) {
            for (String excludedPath : ssoWebSdkProperties.getExcludeds()) {
                String uriPattern = excludedPath.trim();
                if (antPathMatcher.match(uriPattern, requestPath)) {
                    return true;
                }
            }
        }
        return false;
    }

    private void logout(HttpServletRequest httpServletRequest, HttpServletResponse res) {
        try {
            remoteUserAuthorityService.logout(new LogoutBean().setCode(httpServletRequest.getRequestedSessionId()).setApp(ssoWebSdkProperties.getApp()));
            sessionManager.cleanSession(httpServletRequest.getRequestedSessionId());
            if (isJson(httpServletRequest)) {
                res.setContentType("application/json;charset=utf-8");
                Result auth = Result.auth("", "登出系统成功！");
                res.getWriter().println(objectMapper.writeValueAsString(auth));
            } else {
                res.sendRedirect("/");
            }
        } catch (Exception e) {
            log.error("##登出系统失败！", e);
        }
    }

    public boolean isJson(HttpServletRequest httpServletRequest) {
        return Boolean.parseBoolean(httpServletRequest.getHeader("isJson"));
    }

    @Override
    public void destroy() {
        log.info("##拦截器销毁了。。。");
    }

}
